package com.example.module_course.common

import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.util.TypedValue
import android.view.Window
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.foundation.selection.toggleable
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.compose.ui.window.DialogWindowProvider
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupPositionProvider
import androidx.compose.ui.window.PopupProperties
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt

@Composable
internal fun rememberStatusBarHeight(): Int {
    val view = LocalView.current
    return rememberSaveable {
        val windowInsetsCompat = ViewCompat.getRootWindowInsets(view)
        windowInsetsCompat?.getInsets(WindowInsetsCompat.Type.statusBars())?.top ?: 0
    }
}

@Composable
fun rememberActionBarHeight(): Int {
    val context = LocalContext.current
    return rememberSaveable {
        val typedValue = TypedValue()
        if (context.theme.resolveAttribute(android.R.attr.actionBarSize, typedValue, true)) {
            TypedValue.complexToDimensionPixelSize(typedValue.data, context.resources.displayMetrics)
        } else {
            0
        }
    }
}

@Composable
internal fun StatusBarColor(color: Color = Color.Transparent, darkIcon: Boolean = true, fitsSystemWindows: Boolean = true) {
    findWindow()?.apply {
        statusBarColor = color.toArgb()
        WindowCompat.setDecorFitsSystemWindows(this, fitsSystemWindows)
        WindowCompat.getInsetsController(this, LocalView.current).isAppearanceLightStatusBars = darkIcon
    }
}

@Composable
private fun findWindow(): Window? =
    (LocalView.current.parent as? DialogWindowProvider)?.window
        ?: LocalView.current.context.findWindow()

private tailrec fun Context.findWindow(): Window? =
    when (this) {
        is Activity -> window
        is ContextWrapper -> baseContext.findWindow()
        else -> null
    }


private const val MIN_CLICK_DELAY_TIME = 300
private var lastClickTime: Long = 0
private val isFastClick
    get(): Boolean {
        var flag = false
        val curClickTime = System.currentTimeMillis()
        if (curClickTime - lastClickTime <= MIN_CLICK_DELAY_TIME) {
            flag = true
        }
        lastClickTime = curClickTime
        return flag
    }

@SuppressLint("ModifierFactoryUnreferencedReceiver")
internal fun Modifier.click(
    enabled: Boolean = true,
    onClick: () -> Unit = {},
) = composed {
    clickable(
        enabled = enabled,
        interactionSource = remember {
            MutableInteractionSource()
        },
        indication = null
    ) {
        if (!isFastClick) {
            onClick()
        }
    }
}


@Preview
@Composable
private fun CustomTextFieldPreview() {
    var inputText by remember { mutableStateOf("") }
    val maxLenght = 20
    CustomTextField(
        value = inputText,
        onValueChange = {
            inputText = if (it.length > maxLenght) it.substring(startIndex = 0, endIndex = maxLenght) else it
        },
        modifier = Modifier
            .fillMaxWidth()
            .heightIn(min = 41.dp)
            .padding(horizontal = 16.dp)
            .background(Color(color = 0xFFF5F5F8), RoundedCornerShape(21.dp)),
        singleLine = true,
        contentPadding = PaddingValues(horizontal = 16.dp, vertical = 10.dp),
        textStyle = TextStyle(color = Color(0xFF333333), fontSize = 14.sp),
        placeholder = {
            Text(text = "请输入文件夹名称", fontSize = 14.sp, color = Color(0xFF999999))
        },
        rightContent = {
            Text(
                modifier = Modifier
                    .background(if (inputText.isEmpty()) Color(color = 0xFFD6D6D6) else Color(color = 0xFF5D56FB), RoundedCornerShape(21.dp))
                    .click(enabled = inputText.isNotEmpty()) {

                    }
                    .padding(horizontal = 18.dp, vertical = 10.dp),
                text = "确定", fontSize = 14.sp,
                color = Color.White, fontWeight = FontWeight.Medium
            )
        }
    )
}

@Composable
internal fun CustomTextField(
    value: String = "",
    onValueChange: (String) -> Unit,
    modifier: Modifier,
    placeholder: @Composable (() -> Unit)? = null,
    leftContent: @Composable (() -> Unit)? = null,
    rightContent: @Composable (() -> Unit)? = null,
    enabled: Boolean = true,
    readOnly: Boolean = false,
    singleLine: Boolean = false,
    contentPadding: PaddingValues = PaddingValues(),
    boxContainerPadding: PaddingValues = PaddingValues(),
    maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
    minLines: Int = 1,
    textStyle: TextStyle = TextStyle.Default,
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
    keyboardActions: KeyboardActions = KeyboardActions.Default,
    visualTransformation: VisualTransformation = VisualTransformation.None,
    cursorBrush: Brush = SolidColor(Color.Blue),
    decorationBox: @Composable ((innerTextField: @Composable () -> Unit) -> Unit)? = null,
) {
    BasicTextField(
        value = value,
        onValueChange = onValueChange,
        modifier = modifier.defaultMinSize(
            minWidth = 280.dp,
            minHeight = 56.dp
        ),
        singleLine = singleLine,
        maxLines = maxLines,
        minLines = minLines,
        enabled = enabled,
        readOnly = readOnly,
        textStyle = textStyle,
        keyboardOptions = keyboardOptions,
        keyboardActions = keyboardActions,
        visualTransformation = visualTransformation,
        cursorBrush = cursorBrush,
        decorationBox = decorationBox ?: { innerTextField ->
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(paddingValues = boxContainerPadding),
                horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment = Alignment.CenterVertically
            ) {
                if (leftContent != null) {
                    leftContent()
                }
                Box(
                    modifier = Modifier
                        .weight(1f)
                        .padding(paddingValues = contentPadding)
                ) {
                    if (value.isEmpty()) {
                        placeholder?.invoke()
                    }
                    innerTextField()
                }
                if (rightContent != null) {
                    rightContent()
                }
            }
        }
    )
}

@Preview
@Composable
private fun CommonTipsDialogPreview() {
    CommonTipsDialog(
        titleStr = "提示",
        contentStr = "清空后不可撤回，您确定要清空垃圾箱吗？",
        rightButtonStr = "清空",
        modifier = Modifier
            .fillMaxWidth()
            .background(Color.White, RoundedCornerShape(8.dp)),
    )
}

@Composable
internal fun CommonTipsDialog(
    modifier: Modifier = Modifier,
    contentModifier: Modifier = Modifier.padding(top = 32.dp, bottom = 47.dp),
    onDismissRequest: () -> Unit = {},
    titleStr: String = "",
    contentStr: String = "",
    leftButtonStr: String = "取消",
    rightButtonStr: String = "确定",
    leftButtonClick: () -> Unit = {},
    rightButtonClick: () -> Unit = {},
    properties: DialogProperties = DialogProperties(),
    titleContent: @Composable (ColumnScope.() -> Unit)? = null,
    centerContent: @Composable (ColumnScope.() -> Unit)? = null,
    bottomContent: @Composable (ColumnScope.() -> Unit)? = null,
) {
    Dialog(
        properties = properties,
        onDismissRequest = onDismissRequest
    ) {
        Column(modifier = modifier) {
            if (null != titleContent) {
                titleContent()
            } else {
                Text(
                    modifier = Modifier
                        .padding(top = 16.dp)
                        .align(Alignment.CenterHorizontally),
                    text = titleStr,
                    fontWeight = FontWeight.Medium,
                    fontSize = 14.sp,
                    color = Color(0xFF333333)
                )
            }

            if (null != centerContent) {
                centerContent()
            } else {
                Text(
                    modifier = contentModifier
                        .padding(horizontal = 16.dp)
                        .align(Alignment.CenterHorizontally),
                    text = contentStr,
                    fontSize = 14.sp,
                    color = Color(color = 0xFF666666)
                )
            }

            if (null != bottomContent) {
                bottomContent()
            } else {
                Row(
                    modifier = Modifier
                        .padding(horizontal = 32.dp)
                        .align(Alignment.CenterHorizontally)
                        .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween
                ) {
                    Text(
                        modifier = Modifier
                            .clip(RoundedCornerShape(16.dp))
                            .background(Color(color = 0xFFD6D6D6))
                            .click {
                                leftButtonClick()
                            }
                            .padding(horizontal = 20.dp, vertical = 7.dp),
                        text = leftButtonStr,
                        fontWeight = FontWeight.Medium,
                        fontSize = 14.sp,
                        color = Color.White
                    )

                    Text(
                        modifier = Modifier
                            .clip(RoundedCornerShape(16.dp))
                            .background(Color(color = 0xFF5E57FC))
                            .click {
                                rightButtonClick()
                            }
                            .padding(horizontal = 20.dp, vertical = 7.dp),
                        text = rightButtonStr,
                        fontWeight = FontWeight.Medium,
                        fontSize = 14.sp,
                        color = Color.White
                    )
                }
                Spacer(modifier = Modifier.height(24.dp))
            }
        }
    }
}

@Preview
@Composable
private fun LoadingPreview() {
    LoadingDialog("正在处理中...")
}

@Composable
internal fun LoadingDialog(
    content: String,
    modifier: Modifier = ViewExtDefaults.loading,
    dimAmount: Float = 0.5f,
    properties: DialogProperties = DialogProperties(
        dismissOnBackPress = false,
        dismissOnClickOutside = false,
        usePlatformDefaultWidth = false
    ),
    onDismissRequest: () -> Unit = {},
) {
    Dialog(
        onDismissRequest = onDismissRequest,
        properties = properties
    ) {
        val parent = LocalView.current.parent
        if (parent is DialogWindowProvider) {
            parent.window.setDimAmount(dimAmount)
        }
        Column(
            modifier = modifier,
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Spacer(modifier = Modifier.height(27.dp))
            CircularProgressIndicator(
                modifier = Modifier.size(36.dp),
                color = Color(0XFF3E60A2),
                strokeWidth = 4.dp,
                trackColor = Color(0XFFEDEDED),
                strokeCap = StrokeCap.Round
            )
            Spacer(modifier = Modifier.height(18.dp))
            Text(text = content, color = Color(0XFF333333), fontSize = 16.sp, fontWeight = FontWeight.Medium)
            Spacer(modifier = Modifier.height(28.dp))
        }
    }
}

object ViewExtDefaults {
    val loading: Modifier = Modifier
        .width(150.dp)
        .background(Color.White, RoundedCornerShape(12.dp))
}

/**
 * 拷贝出来是为了去掉系统样式
 */
@Composable
internal fun DropdownMenu(
    expanded: Boolean,
    onDismissRequest: () -> Unit,
    offset: DpOffset = DpOffset(0.dp, 0.dp),
    properties: PopupProperties = PopupProperties(focusable = true),
    content: @Composable () -> Unit,
) {
    val expandedStates = remember { MutableTransitionState(false) }
    expandedStates.targetState = expanded

    if (expandedStates.currentState || expandedStates.targetState) {
        val transformOriginState = remember { mutableStateOf(TransformOrigin.Center) }
        val density = LocalDensity.current
        val popupPositionProvider = DropdownMenuPositionProvider(
            offset,
            density
        ) { parentBounds, menuBounds ->
            transformOriginState.value = calculateTransformOrigin(parentBounds, menuBounds)
        }

        Popup(
            onDismissRequest = onDismissRequest,
            popupPositionProvider = popupPositionProvider,
            properties = properties
        ) {
            content()
        }
    }
}

private fun calculateTransformOrigin(
    parentBounds: IntRect,
    menuBounds: IntRect,
): TransformOrigin {
    val pivotX = when {
        menuBounds.left >= parentBounds.right -> 0f
        menuBounds.right <= parentBounds.left -> 1f
        menuBounds.width == 0 -> 0f
        else -> {
            val intersectionCenter =
                (max(parentBounds.left, menuBounds.left) + min(parentBounds.right, menuBounds.right)) / 2
            (intersectionCenter - menuBounds.left).toFloat() / menuBounds.width
        }
    }
    val pivotY = when {
        menuBounds.top >= parentBounds.bottom -> 0f
        menuBounds.bottom <= parentBounds.top -> 1f
        menuBounds.height == 0 -> 0f
        else -> {
            val intersectionCenter =
                (max(parentBounds.top, menuBounds.top) + min(parentBounds.bottom, menuBounds.bottom)) / 2
            (intersectionCenter - menuBounds.top).toFloat() / menuBounds.height
        }
    }
    return TransformOrigin(pivotX, pivotY)
}

@Immutable
private data class DropdownMenuPositionProvider(
    val contentOffset: DpOffset,
    val density: Density,
    val onPositionCalculated: (IntRect, IntRect) -> Unit = { _, _ -> },
) : PopupPositionProvider {
    override fun calculatePosition(
        anchorBounds: IntRect,
        windowSize: IntSize,
        layoutDirection: LayoutDirection,
        popupContentSize: IntSize,
    ): IntOffset {
        // The min margin above and below the menu, relative to the screen.
        val verticalMargin = with(density) { 48.dp.roundToPx() }
        // The content offset specified using the dropdown offset parameter.
        val contentOffsetX = with(density) { contentOffset.x.roundToPx() }
        val contentOffsetY = with(density) { contentOffset.y.roundToPx() }

        // Compute horizontal position.
        val toRight = anchorBounds.left + contentOffsetX
        val toLeft = anchorBounds.right - contentOffsetX - popupContentSize.width
        val toDisplayRight = windowSize.width - popupContentSize.width
        val toDisplayLeft = 0
        val x = if (layoutDirection == LayoutDirection.Ltr) {
            sequenceOf(
                toRight,
                toLeft,
                // If the anchor gets outside of the window on the left, we want to position
                // toDisplayLeft for proximity to the anchor. Otherwise, toDisplayRight.
                if (anchorBounds.left >= 0) toDisplayRight else toDisplayLeft
            )
        } else {
            sequenceOf(
                toLeft,
                toRight,
                // If the anchor gets outside of the window on the right, we want to position
                // toDisplayRight for proximity to the anchor. Otherwise, toDisplayLeft.
                if (anchorBounds.right <= windowSize.width) toDisplayLeft else toDisplayRight
            )
        }.firstOrNull {
            it >= 0 && it + popupContentSize.width <= windowSize.width
        } ?: toLeft

        // Compute vertical position.
        val toBottom = maxOf(anchorBounds.bottom + contentOffsetY, verticalMargin)
        val toTop = anchorBounds.top - contentOffsetY - popupContentSize.height
        val toCenter = anchorBounds.top - popupContentSize.height / 2
        val toDisplayBottom = windowSize.height - popupContentSize.height - verticalMargin
        val y = sequenceOf(toBottom, toTop, toCenter, toDisplayBottom).firstOrNull {
            it >= verticalMargin &&
                    it + popupContentSize.height <= windowSize.height - verticalMargin
        } ?: toTop

        onPositionCalculated(
            anchorBounds,
            IntRect(x, y, x + popupContentSize.width, y + popupContentSize.height)
        )
        return IntOffset(x, y)
    }
}

@Preview
@Composable
private fun SwitchPreview() {
    var checked by remember { mutableStateOf(true) }
    Switch(checked = checked, onCheckedChange = { checked = it })
}

@Composable
internal fun Switch(
    checked: Boolean,
    onCheckedChange: ((Boolean) -> Unit)?,
    enabled: Boolean = true,
    contentPadding: PaddingValues = PaddingValues(),
    trackRadius: DpSize = DpSize(17.dp, 17.dp),
    size: DpSize = DpSize(52.dp, 32.dp),
    checkedTrackColor: Color = Color(0xFFB3ED69),
    uncheckedTrackColor: Color = Color(0xFFD6D6D6),
    checkedThumbColor: Color = Color.White,
    uncheckedThumbColor: Color = checkedThumbColor,
    gapBetweenThumbAndTrackEdge: Dp = 3.dp,
    customTrackContent: @Composable (BoxScope.() -> Unit)? = null,
    customThumbContent: @Composable (BoxScope.() -> Unit)? = null,
) {
    val thumbHeight = (size.height / 2) - gapBetweenThumbAndTrackEdge
    val thumbHeightPx = thumbHeight.dpToPx()
    var onSizeChange by remember { mutableStateOf(IntSize(thumbHeightPx.roundToInt(), thumbHeightPx.roundToInt())) }

    val interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
    val animatePosition = if (customThumbContent != null) {
        animateFloatAsState(
            targetValue = if (checked)
                with(LocalDensity.current) { (size.width - onSizeChange.width.toDp() - gapBetweenThumbAndTrackEdge).toPx() }
            else
                with(LocalDensity.current) { (gapBetweenThumbAndTrackEdge).toPx() }, label = "Switch animatePosition"
        )
    } else {
        animateFloatAsState(
            targetValue = if (checked)
                with(LocalDensity.current) { (size.width - thumbHeight - gapBetweenThumbAndTrackEdge).toPx() }
            else
                with(LocalDensity.current) { (thumbHeight + gapBetweenThumbAndTrackEdge).toPx() }, label = "Switch animatePosition"
        )
    }

    val toggleableModifier =
        if (onCheckedChange != null) {
            Modifier.toggleable(
                value = checked,
                onValueChange = onCheckedChange,
                enabled = enabled,
                role = Role.Switch,
                interactionSource = interactionSource,
                indication = null
            )
        } else {
            Modifier
        }

    Box(
        modifier = Modifier
            .padding(contentPadding)
            .size(size)
            .then(toggleableModifier)
    ) {
        if (customTrackContent != null) {
            customTrackContent()
        } else {
            Canvas(modifier = Modifier.fillMaxSize()) {
                drawRoundRect(
                    color = if (checked) checkedTrackColor else uncheckedTrackColor,
                    cornerRadius = CornerRadius(x = trackRadius.width.toPx(), y = trackRadius.height.toPx()),
                )
            }
        }

        if (customThumbContent != null) {
            Box(
                modifier = Modifier
                    .height(thumbHeight * 2)
                    .widthIn(min = thumbHeight * 2, max = size.width / 2 - gapBetweenThumbAndTrackEdge * 2)
                    .onSizeChanged { onSizeChange = it }
                    .offset { Offset(x = animatePosition.value, y = gapBetweenThumbAndTrackEdge.toPx()).round() }
            ) {
                customThumbContent()
            }
        } else {
            Canvas(modifier = Modifier.fillMaxSize()) {
                drawCircle(
                    color = if (checked) checkedThumbColor else uncheckedThumbColor,
                    radius = thumbHeight.toPx(),
                    center = Offset(
                        x = animatePosition.value,
                        y = this.size.height / 2
                    )
                )
            }
        }
    }
}

@Composable
internal fun ExpandableContainerView(modifier: Modifier, expanded: Boolean, headerContent: @Composable ColumnScope.() -> Unit, expandContent: @Composable ColumnScope.() -> Unit) {
    Column(modifier = modifier) {
        headerContent()
        ExpandableView(expanded = expanded, expandContent)
    }
}


@Preview(showBackground = true)
@Composable
internal fun ExpandableContainerViewPreview() {
    val data = (1..10).toList()
    var expanded by remember { mutableStateOf(false) }
    ExpandableContainerView(
        modifier = Modifier.fillMaxWidth(),
        expanded = expanded,
        headerContent = {
            Text(modifier = Modifier
                .click { expanded = !expanded }
                .padding(16.dp), text = "我是标题")
        },
        expandContent = {
            LazyColumn {
                items(data) {
                    Text(
                        modifier = Modifier
                            .fillMaxWidth()
                            .background(Color.LightGray)
                            .padding(horizontal = 16.dp, vertical = 4.dp),
                        text = "我是展开 $it"
                    )
                }
            }
        }
    )
}

@Composable
internal fun ExpandableView(expanded: Boolean, content: @Composable ColumnScope.() -> Unit) {
    val expandTransition = remember {
        expandVertically(
            expandFrom = Alignment.Top,
            animationSpec = tween(300)
        ) + fadeIn(
            animationSpec = tween(300)
        )
    }

    val collapseTransition = remember {
        shrinkVertically(
            shrinkTowards = Alignment.Top,
            animationSpec = tween(300)
        ) + fadeOut(
            animationSpec = tween(300)
        )
    }

    AnimatedVisibility(
        visible = expanded,
        enter = expandTransition,
        exit = collapseTransition
    ) {
        Column {
            content()
        }
    }
}

@Composable
internal fun RadioGroup(
    modifier: Modifier = Modifier,
    radioButtons: List<@Composable BoxScope.(selected: Boolean) -> Unit>,
    state: RadioGroupState = rememberRadioGroupState(),
    spacing: Dp = 0.dp,
    enabled: Boolean = true,
) {
    require(radioButtons.isNotEmpty()) {
        "radioButtons 不能为空"
    }

    when (state.orientation) {
        RadioGroupState.Orientation.Horizontal -> {
            Row(
                modifier = modifier
                    .selectableGroup(),
                horizontalArrangement = Arrangement.spacedBy(spacing)
            ) {
                RadioGroupImpl(radioButtons, state, enabled)
            }
        }

        RadioGroupState.Orientation.Vertical -> {
            Column(
                modifier = Modifier
                    .selectableGroup(),
                verticalArrangement = Arrangement.spacedBy(spacing)
            ) {
                RadioGroupImpl(radioButtons, state, enabled)
            }
        }
    }
}

@Composable
private fun RadioGroupImpl(
    radioButtons: List<@Composable (BoxScope.(selected: Boolean) -> Unit)>,
    state: RadioGroupState,
    enabled: Boolean,
) {
    radioButtons.forEachIndexed { index, radioButton ->
        Box(
            modifier = Modifier
                .selectable(
                    selected = state.currentSelectedOption == index,
                    onClick = { state.currentSelectedOption = index },
                    indication = null,
                    enabled = enabled,
                    interactionSource = remember { MutableInteractionSource() },
                    role = Role.RadioButton
                )
        ) {
            radioButton(state.currentSelectedOption == index)
        }
    }
}

@Composable
internal fun rememberRadioGroupState(
    initSelectedOption: Int = 0,
    initOrientation: RadioGroupState.Orientation = RadioGroupState.Orientation.Horizontal,
): RadioGroupState {
    return rememberSaveable(saver = RadioGroupState.Saver) {
        RadioGroupState(initSelectedOption, initOrientation)
    }
}

@Stable
internal class RadioGroupState(
    val initSelectedOption: Int,
    val orientation: Orientation,
) {
    var currentSelectedOption: Int by mutableStateOf(initSelectedOption)

    companion object {
        val Saver: Saver<RadioGroupState, *> = listSaver(
            save = {
                listOf(
                    it.orientation,
                    it.currentSelectedOption
                )
            },
            restore = {
                RadioGroupState(
                    orientation = it[0] as Orientation,
                    initSelectedOption = it[1] as Int,
                )
            }
        )
    }

    enum class Orientation {
        Horizontal,
        Vertical,
    }
}

@Composable
internal fun <T> rememberUpdatedMutableState(newValue: T): MutableState<T> = remember {
    mutableStateOf(newValue)
}.apply { value = newValue }

@Composable
internal fun Dp.dpToPx() = with(LocalDensity.current) { this@dpToPx.toPx() }

@Composable
internal fun Int.pxToDp() = with(LocalDensity.current) { this@pxToDp.toDp() }

@Composable
internal fun Float.pxToDp() = with(LocalDensity.current) { this@pxToDp.toDp() }
